<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>15.5.1-将模板 AST 转为 JavaScript AST</title>
</head>
<body>
    
</body>
    <script>
        /**
         * 为什么要将模板 AST 转换为 JavaScript AST 呢？
         * - 需要将模板编译为渲染函数
         * - 渲染函数是由 JavaScript 代码来描述的，
         *    因此，我们需要将模板 AST 转换为用于描述渲染函数的 JavaScript AST
         * 
         * JavaScript AST 是什么样子的呢？
         * - 与模板 AST 是模板的描述一样，JavaScript AST 是 JavaScript 代码的描述
         * - 本质上我们需要设计一些数据结构来描述渲染函数的代码
         * 
         * 
         * 模板为例:
         *  <div><p>Vue</p><p>Template</p></div>
         * 
         * 等价的渲染函数是：
         *  function render() {
         *      return h('div', [h('p', 'Vue'), h('p', 'Template')])
         *  }
        */


        /**
         * 一个函数声明语句由以下几部分组成
         * - id：函数名称，它是一个标识符 Identifier
         * - params：函数的参数，它是一个数组
         * - body：函数体，由于函数体可以包含多个语句，因此它也是一个数组
         * 
         * 一个基本的数据结构来描述函数声明语句:
         * 
         * ex -> function render() {}
         * */ 
        
         const _FunctionDeclNode = {
            type: 'FunctionDecl', // 代表该节点是函数声明
            // 函数的名称是一个标识符，标识符本身也是一个节点
            id: {
                type: 'Identifier',
                name: 'render' // name 用来存储标识符的名称，在这里它就是渲染函数的名称 render
            },
            params: [], // 参数，目前渲染函数还不需要参数，所以这里是一个空数组
            // 渲染函数的函数体只有一个语句，即 return 语句
            body: [
                {
                    type: 'ReturnStatement',
                    return: null // 暂时留空，在后续讲解中补全
                }
            ]
        }

        /**
         * 渲染函数的返回值
         * - 渲染函数返回的是虚拟 DOM 节点，具体体现在 h 函数的调用
         * - 可以使用 CallExpression 类型的节点来描述函数调用语句
         * -
         * 
         * 类型为 CallExpression 的节点拥有两个属性
         * - callee：用来描述被调用函数的名字称
         * - arguments：被调用函数的形式参数，多个参数的话用数组来描述
         * 
         * ex -> h()
         * */ 

        const CallExp = {
            type: 'CallExpression',
            // 被调用函数的名称，它是一个标识符
            callee: {
                type: 'Identifier',
                name: 'h'
            },
            // 参数
            arguments: []
        }

        /**
         * - 最外层的 h 函数的第一个参数是一个字符串字面量，我们可以使用类型为StringLiteral 的节点来描述它
         * - h 函数的第二个参数是一个数组，我们可以使用类型为 ArrayExpression 的节点来描述
         * 
         * ex -> h('div', [])
        */

        const Str = {
            type: 'StringLiteral',
            value: 'div'
        }

        const ArrayExp = {
            type: 'ArrayExpression',
            // 数组中的元素
            elements: []
        }

        /**
         * 使用上述 CallExpression、StringLiteral、ArrayExpression 等节点
         *  来填充渲染函数的返回值
         * 
         * FunctionDeclNode 这段 JavaScript AST 的代码
         *  是对渲染函数（render）代码的完整描述
         * 
         * ex -> function render() { return h('div', [h('p', 'Vue'), h('p', 'Template')]) }
         * */ 

        const FunctionDeclNode = {
            type: 'FunctionDecl', // 代表该节点是函数声明
            // 函数的名称是一个标识符，标识符本身也是一个节点
            id: {
                type: 'Identifier',
                name: 'render' // name 用来存储标识符的名称，在这里它就是渲染函数的名称 render
            },
            params: [], // 参数，目前渲染函数还不需要参数，所以这里是一个空数组
            // 渲染函数的函数体只有一个语句，即 return 语句
            body: [
                {
                    type: 'ReturnStatement',
                    // 最外层的 h 函数调用
                    return: {
                        type: 'CallExpression',
                        callee: { type: 'Identifier', name: 'h' },
                        arguments: [
                            // 第一个参数是字符串字面量 'div'
                            {
                                type: 'StringLiteral',
                                value: 'div'
                            },
                            // 第二个参数是一个数组
                            {
                                type: 'ArrayExpression',
                                elements: [
                                    // 数组的第一个元素是 h 函数的调用
                                    {
                                        type: 'CallExpression',
                                        callee: { type: 'Identifier', name: 'h' },
                                        arguments: [
                                            // 该 h 函数调用的第一个参数是字符串字面量
                                            { type: 'StringLiteral', value: 'p' },
                                            // 第二个参数也是一个字符串字面量
                                            { type: 'StringLiteral', value: 'Vue' },
                                        ]
                                    },
                                    // 数组的第二个元素也是 h 函数的调用
                                    {
                                        type: 'CallExpression',
                                        callee: { type: 'Identifier', name: 'h' },
                                        arguments: [
                                            // 该 h 函数调用的第一个参数是字符串字面量
                                            { type: 'StringLiteral', value: 'p' },
                                            // 第二个参数也是一个字符串字面量
                                            { type: 'StringLiteral', value: 'Template' },
                                        ]
                                    }
                                ]
                            }
                        ]
                    }
                }
            ]
        }






        
    </script>
    

</html>